package com.opNaga.bpmn.palettes;

import com.yworks.yfiles.bpmn.view.BpmnRealizerFactory;
import com.yworks.yfiles.bpmn.view.BpmnTypeEnum;
import com.yworks.yfiles.bpmn.view.EventCharEnum;
import y.geom.YPoint;
import y.option.RealizerCellRenderer;
import y.view.*;
import y.view.tabular.TableGroupNodeRealizer;

import javax.swing.*;
import java.awt.*;
import java.awt.geom.Ellipse2D;
import java.awt.geom.RectangularShape;
import java.awt.geom.RoundRectangle2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class WorkflowPalette extends JComponent {
    private DragAndDropSupport dropSupport;

    public WorkflowPalette(final Graph2DView view) {
        final BorderLayout borderLayout = new BorderLayout();
        borderLayout.setVgap(10);
        this.setLayout(borderLayout);
        this.add(createDefaultPalette(view), BorderLayout.CENTER);
        initializeDefaultRealizers(view);
    }

    protected void initializeDefaultRealizers(Graph2DView view) {
        Graph2D graph = view.getGraph2D();
        final EdgeRealizer der = graph.getDefaultEdgeRealizer();
        if (der instanceof PolyLineEdgeRealizer) {
            ((PolyLineEdgeRealizer) der).setSmoothedBends(true);
            der.setTargetArrow(Arrow.STANDARD);
        }
    }

    private JComponent createDefaultPalette(final Graph2DView view) {

        final ArrayList realizers = new ArrayList();
        addDefaultTemplates(realizers);

        dropSupport = new DragAndDropSupport(realizers, view);
        final JList realizerList = dropSupport.getList();
        realizerList.setCellRenderer(new RealizerCellRenderer(60, 45) {
            private Map<NodeRealizer, String> nodeTips = new HashMap<NodeRealizer, String>();
            private Map<EdgeRealizer, String> edgeTips = new HashMap<EdgeRealizer, String>();

            protected Icon createEdgeRealizerIcon(EdgeRealizer realizer, int iconWidth, int iconHeight) {
                final EdgeRealizerIcon icon = createIcon(realizer, iconWidth, iconHeight);
                icon.setDrawingBends(false);
                return icon;
            }

            private EdgeRealizerIcon createIcon(EdgeRealizer realizer, int iconWidth, int iconHeight) {
                if (realizer.labelCount() > 0) {
                    final String text = realizer.getLabelText();
                    if ("No".equalsIgnoreCase(text) || "Yes".equalsIgnoreCase(text)) {
                        return new EdgeRealizerIcon(realizer, iconWidth, iconHeight) {
                            protected YPoint calculateSourceBend(EdgeRealizer realizer, int iconWidth, int iconHeight) {
                                return new YPoint(0.5 * iconWidth, iconHeight - realizer.getLabel().getHeight() - 2);
                            }
                        };
                    }
                }
                return new EdgeRealizerIcon(realizer, iconWidth, iconHeight);
            }

            protected String createEdgeToolTipText(EdgeRealizer realizer) {
                if (edgeTips.containsKey(realizer)) {
                    return edgeTips.get(realizer);
                } else {
                    final String text = createEdgeToolTip(realizer);
                    edgeTips.put(realizer, text);
                    return text;
                }
            }

            protected String createNodeToolTipText(final NodeRealizer realizer) {
                if (nodeTips.containsKey(realizer)) {
                    return nodeTips.get(realizer);
                } else {
                    final String text = createNodeToolTip(realizer);
                    nodeTips.put(realizer, text);
                    return text;
                }
            }
        });
        realizerList.setLayoutOrientation(JList.HORIZONTAL_WRAP);
        realizerList.setVisibleRowCount(-1);
        JScrollPane palette = new JScrollPane(realizerList);
        palette.setPreferredSize(new Dimension(220, 300));
        return palette;
    }

    protected String createEdgeToolTip(final EdgeRealizer realizer) {
        return null;
    }

    protected String createNodeToolTip(final NodeRealizer realizer) {
        if (realizer instanceof GenericNodeRealizer) {
            String s = ((GenericNodeRealizer) realizer).getConfiguration();
            if (s != null) {
                s = s.trim();
                final String prefix = "com.yworks.flowchart.";
                if (s.startsWith(prefix)) {
                    s = s.substring(prefix.length());
                    final int l = s.length();
                    final char[] chars = s.toCharArray();
                    final StringBuffer sb = new StringBuffer(s.length() + 4);
                    int last = 0;
                    String del = "";
                    for (int i = 1; i < l; ++i) {
                        if (Character.isUpperCase(chars[i])) {
                            sb.append(del).append(Character.toUpperCase(chars[last]));
                            sb.append(chars, last + 1, i - last - 1);
                            last = i;
                            del = " ";
                        }
                    }
                    if (last < l) {
                        sb.append(del).append(Character.toUpperCase(chars[last]));
                        sb.append(chars, last + 1, l - last - 1);
                    }
                    return sb.toString();
                }
            }
        }
        return null;
    }

    protected void addDefaultTemplates(final List realizers) {
        addEvents(realizers);
        addGateways(realizers);
        addArtifacts(realizers);
        addActivities(realizers);
        addPools(realizers);
        addRelations(realizers);

        UMLClassNodeRealizer r = new UMLClassNodeRealizer();
        r.setClassName("com.mycompany.MyClass");
        r.setConstraint("abstract");
        r.setStereotype("factory");
        r.addAttribute("-graph");
        r.addAttribute("-id");
        r.addMethod("+setGraph(Graph)");
        r.addMethod("+getGraph():Graph");
        r.addMethod("+setID(int)");
        r.addMethod("+getID():int");
        r.fitContent();
        realizers.add(r);
    }

    protected void addPools(List<NodeRealizer> paramList) {
        TableGroupNodeRealizer poolRealizer = BpmnRealizerFactory.createPool(BpmnTypeEnum.POOL_TYPE_EMPTY);
        poolRealizer.setFillColor(new Color(255, 242, 188));
        poolRealizer.setStyleProperty("POOL_LANE_STYLE", "LANE_STYLE_ROWS");
        poolRealizer.setStyleProperty("POOL_LANE_COLOR_MAIN", Color.WHITE);
        poolRealizer.setStyleProperty("POOL_LANE_COLOR_ALTERNATING", Color.WHITE);
        poolRealizer.setLineType(LineType.LINE_1);
        paramList.add(poolRealizer);
        TableGroupNodeRealizer laneRealizer = BpmnRealizerFactory.createPool(BpmnTypeEnum.POOL_TYPE_LANE);
        laneRealizer.setFillColor(new Color(255, 242, 188));
        laneRealizer.setStyleProperty("POOL_LANE_STYLE", "LANE_STYLE_ROWS");
        laneRealizer.setStyleProperty("POOL_LANE_COLOR_MAIN", Color.WHITE);
        laneRealizer.setStyleProperty("POOL_LANE_COLOR_ALTERNATING", Color.WHITE);
        laneRealizer.setLineType(LineType.LINE_1);
        paramList.add(laneRealizer);
    }

    protected void addRelations(List<EdgeRealizer> paramList) {
        EdgeRealizer sequenceFlow = BpmnRealizerFactory.createConnection(BpmnTypeEnum.CONNECTION_TYPE_SEQUENCE_FLOW);
        paramList.add(sequenceFlow);
        EdgeRealizer messageFlow = BpmnRealizerFactory.createConnection(BpmnTypeEnum.CONNECTION_TYPE_MESSAGE_FLOW);
        paramList.add(messageFlow);
        EdgeRealizer associationFlow = BpmnRealizerFactory.createConnection(BpmnTypeEnum.CONNECTION_TYPE_DIRECTED_ASSOCIATION);
        paramList.add(associationFlow);
    }

    protected void addArtifacts(List<NodeRealizer> realizerList) {
        NodeRealizer dataObject = BpmnRealizerFactory.createArtifact(BpmnTypeEnum.ARTIFACT_TYPE_DATA_OBJECT);
        BpmnRealizerFactory.setShadowEnabled(dataObject, true);
        dataObject.setSize(35.0D, 55.0D);
        dataObject.setFillColor(new Color(255, 255, 255, 230));
        dataObject.setFillColor2(new Color(212, 212, 212, 204));
        dataObject.setLineColor(Color.BLACK);
        realizerList.add(dataObject);
        NodeRealizer annotation = BpmnRealizerFactory.createArtifact(BpmnTypeEnum.ARTIFACT_TYPE_ANNOTATION);
        BpmnRealizerFactory.setShadowEnabled(annotation, true);
        annotation.setSize(80.0D, 30.0D);
        annotation.setFillColor(new Color(255, 255, 255, 230));
        annotation.setFillColor2(new Color(212, 212, 212, 204));
        annotation.setLineColor(Color.BLACK);
        realizerList.add(annotation);
        NodeRealizer group = BpmnRealizerFactory.createArtifact(BpmnTypeEnum.ARTIFACT_TYPE_GROUP);
        BpmnRealizerFactory.setShadowEnabled(group, true);
        group.setSize(85.0D, 55.0D);
        group.setFillColor(new Color(255, 255, 255, 230));
        group.setFillColor2(new Color(212, 212, 212, 204));
        group.setLineColor(Color.BLACK);
        realizerList.add(group);
    }

    protected void addEvents(List<NodeRealizer> realizerList) {
        NodeRealizer startEvent = BpmnRealizerFactory.createEvent(BpmnTypeEnum.EVENT_TYPE_PLAIN, EventCharEnum.EVENT_CHARACTERISTIC_START);
        startEvent.setFillColor(new Color(255, 255, 255, 230));
        startEvent.setFillColor2(new Color(212, 212, 212, 204));
        startEvent.setLineColor(new Color(39, 174, 39));
        BpmnRealizerFactory.setShadowEnabled(startEvent, true);
        realizerList.add(startEvent);
        NodeRealizer intermediateEvent = BpmnRealizerFactory.createEvent(BpmnTypeEnum.EVENT_TYPE_PLAIN, EventCharEnum.EVENT_CHARACTERISTIC_INTERMEDIATE_CATCHING);
        intermediateEvent.setFillColor(new Color(255, 255, 255, 230));
        intermediateEvent.setFillColor2(new Color(212, 212, 212, 204));
        intermediateEvent.setLineColor(new Color(220, 186, 0));
        BpmnRealizerFactory.setShadowEnabled(intermediateEvent, true);
        realizerList.add(intermediateEvent);
        NodeRealizer endEvent = BpmnRealizerFactory.createEvent(BpmnTypeEnum.EVENT_TYPE_PLAIN, EventCharEnum.EVENT_CHARACTERISTIC_END);
        endEvent.setFillColor(new Color(255, 255, 255, 230));
        endEvent.setFillColor2(new Color(212, 212, 212, 204));
        endEvent.setLineColor(new Color(177, 31, 31));
        BpmnRealizerFactory.setShadowEnabled(endEvent, true);
        realizerList.add(endEvent);
    }

    protected void addGateways(List<NodeRealizer> realizerList) {
        NodeRealizer xorGate = BpmnRealizerFactory.createGateway(BpmnTypeEnum.GATEWAY_TYPE_DATA_BASED_EXCLUSIVE);
        BpmnRealizerFactory.setShadowEnabled(xorGate, true);
        xorGate.setSize(45.0D, 45.0D);
        xorGate.setFillColor(new Color(255, 255, 255, 230));
        xorGate.setFillColor2(new Color(212, 212, 212, 204));
        realizerList.add(xorGate);
        NodeRealizer orGate = BpmnRealizerFactory.createGateway(BpmnTypeEnum.GATEWAY_TYPE_INCLUSIVE);
        BpmnRealizerFactory.setShadowEnabled(orGate, true);
        orGate.setSize(45.0D, 45.0D);
        orGate.setFillColor(new Color(255, 255, 255, 230));
        orGate.setFillColor2(new Color(212, 212, 212, 204));
        realizerList.add(orGate);
        NodeRealizer andGate = BpmnRealizerFactory.createGateway(BpmnTypeEnum.GATEWAY_TYPE_PARALLEL);
        BpmnRealizerFactory.setShadowEnabled(andGate, true);
        andGate.setSize(45.0D, 45.0D);
        andGate.setFillColor(new Color(255, 255, 255, 230));
        andGate.setFillColor2(new Color(212, 212, 212, 204));
        realizerList.add(andGate);
    }

    protected void addActivities(List<NodeRealizer> realizerList) {
        NodeRealizer task = BpmnRealizerFactory.createActivity(BpmnTypeEnum.ACTIVITY_TYPE_STATELESS_TASK_PLAIN);
        task.setFillColor(new Color(255, 255, 255, 230));
        task.setFillColor2(new Color(212, 212, 212, 204));
        task.setLineColor(Color.BLACK);
        BpmnRealizerFactory.setShadowEnabled(task, true);
        task.setSize(85.0D, 55.0D);
        realizerList.add(task);
    }

    static final class CustomHotSpotPainter extends AbstractCustomHotSpotPainter {
        private RectangularShape shape;
        private Color color;

        CustomHotSpotPainter(int mask, RectangularShape shape, Color color) {
            super(mask);
            this.shape = shape;
            this.color = color;
        }

        protected void initGraphics(NodeRealizer context, Graphics2D g) {
            super.initGraphics(context, g);
            if (color == null) {
                Color fc = context.getFillColor();
                if (fc != null) {
                    g.setColor(fc);
                }
            } else {
                g.setColor(color);
            }
        }

        protected void paint(byte hotSpot, double centerX, double centerY, Graphics2D graphics) {
            shape.setFrame(centerX - 2, centerY - 2, 5, 5);
            graphics.fill(shape);
        }

        protected boolean isHit(byte hotSpot, double centerX, double centerY, double testX, double testY) {
            return Math.abs(testX - centerX) < 3 && Math.abs(testY - centerY) < 3;
        }
    }
}